#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QTimer>
#include <QSqlRecord>
#include <QDebug>
#include <QModbusTcpClient>
#include <QModbusRtuSerialMaster>
#include <QStandardItemModel>
#include <QStatusBar>
#include <QSerialPort>
#include <QSoundEffect>

bool checkBuy();

enum ModbusConnection {
    Serial,
    Tcp
};

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    timer = new QTimer(this);
    if(checkBuy())
        connect(timer, SIGNAL(timeout()),this,SLOT(time_update()));
    savetimer = new QTimer(this);
    connect(savetimer, SIGNAL(timeout()),this,SLOT(deleteRecoder()));

    if(!createConnection()) {
        QMessageBox::critical(this, tr("数据库创建失败"), tr("数据库创建或打开失败！"), QMessageBox::Ok);
        return;
    }
    model = new QSqlQueryModel(this);
    model->setQuery("select datetime(numtime, 'unixepoch', 'localtime'), numvalue, numunit "
                    "from concentration "
                    "where chname = 'CHA1' "
                    "order by numtime desc "
                    "limit 0, 20");
    model->setHeaderData(0, Qt::Horizontal, tr("日期"));
    model->setHeaderData(1, Qt::Horizontal, tr("数值"));
    model->setHeaderData(2, Qt::Horizontal, tr("单位"));
    QSortFilterProxyModel *sqlproxy = new QSortFilterProxyModel(this);
    sqlproxy->setSourceModel(model);
    ui->tableView->setModel(sqlproxy);
    ui->tableView->setEditTriggers(QAbstractItemView::NoEditTriggers);
    ui->tableView->setColumnWidth(0, 150);
    ui->tableView->setColumnWidth(1, 30);
    ui->tableView->setColumnWidth(2, 40);

    ui->tableView->hideColumn(3);
    ui->tableView->horizontalHeader()->setSectionResizeMode(1,QHeaderView::Stretch);

    alarmmodel = new QSqlQueryModel(this);
    alarmmodel->setQuery("select datetime(numtime, 'unixepoch', 'localtime'), chname, numvalue from alarm order by numtime desc limit 0, 20");
    alarmmodel->setHeaderData(0, Qt::Horizontal, tr("时间"));
    alarmmodel->setHeaderData(1, Qt::Horizontal, tr("卡槽号"));
    alarmmodel->setHeaderData(2, Qt::Horizontal, tr("事件内容"));
    QSortFilterProxyModel *sqlproxyA = new QSortFilterProxyModel(this);
    sqlproxyA->setSourceModel(alarmmodel);
    ui->tableViewAlarm->setModel(sqlproxyA);
    ui->tableViewAlarm->setEditTriggers(QAbstractItemView::NoEditTriggers);
    ui->tableViewAlarm->setColumnWidth(0, 150);
    ui->tableViewAlarm->setColumnWidth(1, 100);

    ui->tableViewAlarm->horizontalHeader()->setSectionResizeMode(2,QHeaderView::Stretch);


    radioButton = new QButtonGroup(this);
    //向绘图区域QCustomPlot(从widget提升来的)添加一条曲线
    for(int i=0; i<MSA9020_COUNT; i++) {
        // 设置显示曲线的信号槽
        QRadioButton *chx;
        chx = this->findChild<QRadioButton*>("disp_plotA"+QString::number(i+1));
        radioButton->addButton(chx, i);
        connect(chx, SIGNAL(clicked(bool)), this, SLOT(dispPlot()));
        chx->setProperty("alarmStatus",false);

        chx = this->findChild<QRadioButton*>("disp_plotB"+QString::number(i+1));
        radioButton->addButton(chx, i+MSA9020_COUNT);
        connect(chx, SIGNAL(clicked(bool)), this, SLOT(dispPlot()));

        chx->setProperty("alarmStatus",false);

        QFrame *f;
        f = this->findChild<QFrame *>("frame_"+QString::number(i+1));
        f->setStyleSheet("background-color: rgb(7, 34, 31);border-radius:4px;");


    }
    // 显示数据库数据曲线
    mGraphDB = ui->mPlot->addGraph();
    mGraphDB->setPen(QPen(Qt::red));
    mGraphDB->setVisible(true);

    //设置坐标轴标签名称
    ui->mPlot->xAxis->setLabel("时间");
    ui->mPlot->yAxis->setLabel("浓度");
    ui->mPlot->yAxis->setRange(-100, 100);
    QDateTime dateTime = QDateTime::currentDateTime();
    uint  now = dateTime.toTime_t();//当前时间转化为秒
    //生成时间刻度对象
    QSharedPointer<QCPAxisTickerDateTime> dateTimeTicker(new QCPAxisTickerDateTime);
    ui->mPlot->xAxis->setTicker(dateTimeTicker);

    dateTimeTicker->setTickCount(24);
    dateTimeTicker->setTickStepStrategy(QCPAxisTicker::tssMeetTickCount);
    ui->mPlot->xAxis->setSubTicks(false);
    ui->mPlot->xAxis->setRange(now-3600*XAXIS_RANGE, now);//, Qt::AlignRight);//x轴范围，从当前时间起往后推24小时
    dateTimeTicker->setDateTimeFormat("MM-dd hh:mm");//设置x轴刻度显示格式
    ui->mPlot->xAxis->setTickLabelRotation(60);//设置刻度标签顺时针旋转30度
    ui->mPlot->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom | QCP::iSelectPlottables);
    ui->mPlot->axisRect()->setRangeDrag(Qt::Horizontal);
    ui->mPlot->axisRect()->setRangeZoom(Qt::Horizontal);

    plotTitleName = new QCPTextElement(ui->mPlot, "实时趋势图", QFont("Microsoft Yahei", 14, QFont::Bold));
    ui->mPlot->plotLayout()->insertRow(0);
    ui->mPlot->plotLayout()->addElement(plotTitleName);

    connect(ui->mPlot, SIGNAL(mouseMove(QMouseEvent*)), this, SLOT(mPlotMoveEvent(QMouseEvent*)));

    // 状态栏：通讯
    ComStatic = new QLabel("通讯端口状态", this);
    ComStatic->setMinimumWidth(150);
    ComStatic->setAlignment(Qt::AlignCenter);  //水平居中(HCenter)。
    statusBar()->addPermanentWidget(ComStatic); //现实永久信息
    statusBar()->showMessage(this->windowTitle());

    ledTx = new QLabel("<font>&#9650;</font>", this);
    ledTx->setAlignment(Qt::AlignCenter);  //水平居中(HCenter)。
    ledTx->setStyleSheet("color:rgb(190,190,190);");//改成 灰色圆形
    statusBar()->addPermanentWidget(ledTx);
    ledTxOff = new QTimer(this);                                        //创建定时器
    connect(ledTxOff, &QTimer::timeout, this, [=]() {
        ledTx->setStyleSheet("color:rgb(190,190,190);");
        ledTxOff->stop();
    });

    ledRx = new QLabel("<font>&#9660;</font>", this);
    ledRx->setAlignment(Qt::AlignCenter);  //水平居中(HCenter)。
    ledRx->setStyleSheet("color:rgb(190,190,190);");//改成 灰色圆形
    statusBar()->addPermanentWidget(ledRx);
    ledRxOff = new QTimer(this);                                        //创建定时器
    connect(ledRxOff, &QTimer::timeout, this, [=]() {
        ledRx->setStyleSheet("color:rgb(190,190,190);");
        ledRxOff->stop();
    });

    // 状态栏：时钟
    ClockStatic = new QLabel("", this);
    ClockStatic->setMinimumWidth(140);
    ClockStatic->setAlignment(Qt::AlignCenter);  //水平居中(HCenter)。
    statusBar()->addPermanentWidget(ClockStatic); //现实永久信息
    clocktime = new QTimer(this);
    connect(clocktime, &QTimer::timeout,[this]() {
        QDateTime t = QDateTime::currentDateTime();
        ClockStatic->setText(t.toString("yyyy-MM-dd hh:mm:ss"));
    });
    clocktime->start(1000);

    // 状态栏：版权
    QLabel *Copyright = new QLabel("<font>Copyright&#169; 2019-2020</font> 武汉中祺科技", this);
    Copyright->setMinimumWidth(270);
    Copyright->setAlignment(Qt::AlignCenter);  //水平居中(HCenter)。
    statusBar()->addPermanentWidget(Copyright); //现实永久信息

    dlgvol = new DialogVol;
    dlgvol->setWindowFlags(Qt::Dialog | Qt::FramelessWindowHint);
    connect(dlgvol, SIGNAL(setvol(int)), this, SLOT(setvol(int)));
    //dlgvol->initVol(configIni.value("sys/Volume", 50).toInt());

    // 报警音
    player = new QMediaPlayer;
    playlist = new QMediaPlaylist(player);
    playlist->setPlaybackMode(QMediaPlaylist::CurrentItemOnce); // 播放1次
    dir=new QDir("/home/pi/Music");
    QStringList filter;
    filter<<QString("*.mp3");
    fileInfo=new QList<QFileInfo>(dir->entryInfoList(filter));

    initfrom();

#ifndef _DEBUG_MODE
    this->showFullScreen();  // 全屏显示
#endif

    connectSlave();
}

MainWindow::~MainWindow()
{
    if (modbusDevice) {
        modbusDevice->disconnectDevice();
        delete modbusDevice;
        modbusDevice = nullptr;
    }

    QSettings configIni(QCoreApplication::applicationDirPath()+"/config.ini", QSettings::IniFormat);

    configIni.setValue("sys/Volume", volume);

    delete ui;
}

void MainWindow::initfrom()
{
    // 创建MODBUS连接
    //if(!modbusDevice) {
    modbusDevice = new QModbusRtuSerialMaster(this);
    connect(modbusDevice, &QModbusClient::errorOccurred, [this](QModbusDevice::Error) {
        statusBar()->showMessage(modbusDevice->errorString(), 5000);
    });
    connect(modbusDevice, &QModbusClient::stateChanged,
            this, &MainWindow::onStateChanged);
    //}

    QSettings configIni(QCoreApplication::applicationDirPath()+"/config.ini", QSettings::IniFormat);

    m.initMsa(QCoreApplication::applicationDirPath()+"/config.ini", this);

    saveday = configIni.value("sys/day", 7).toInt();
    savetimer->start(24*60*60000);

    dispPlot();

    playlist->clear();
    for(int i = 0; i<fileInfo->count(); i++)
    {
        //qDebug()<<fileInfo->at(i).filePath();
        //qDebug()<<fileInfo->at(i).fileName();
        playlist->addMedia(QUrl::fromLocalFile(fileInfo->at(i).filePath()));
    }
    player->setPlaylist(playlist);
    volume = configIni.value("sys/Volume", 50).toInt();
    dlgvol->initVol(volume);

#ifdef _DEBUG_MODE
    ui->actionTEST->setVisible(true);
#else
    ui->actionTEST->setVisible(false);
#endif
}

void MainWindow::time_update()
{
    //读MSA卡的数值
    foreach(quint8 i,m.cards.keys())
        readMsaData(i);
}

void MainWindow::writeLog(QString w_r, char *buf, int len)
{

    QString s;
    for(int i=0; i<len; i++)
        s += QString().asprintf("%02x ",buf[i]);
    s = w_r +"("+QString::number(len)+"): "+ s + "\n";
}

void MainWindow::on_action_openCom_triggered()
{
    connectSlave();
}

void MainWindow::on_action_closeCom_triggered()
{
    //connectSlave();
    modbusDevice->disconnectDevice();
    //modbusDevice->disconnect();
}

void MainWindow::on_action_options_triggered()
{
    options *opt = new options;
    opt->setWindowModality(Qt::ApplicationModal); //顶端窗口
    connect(opt, SIGNAL(sendData()), this, SLOT(refreshInifile()));
    opt->add_mp3(fileInfo);
    opt->show();
}

// 更新设置
void MainWindow::refreshInifile()
{
    m.initMsa(QCoreApplication::applicationDirPath()+"/config.ini", this);
// 数据保存时间
    saveday = configIni.value("sys/day", 7).toInt();

    bool b = timer->isActive();
    if(b)
        timer->stop();
    //读MSA卡的单位
    foreach(quint8 i,m.cards.keys())
        readMsaUnit(i);
    if(b)
        timer->start();
}

void MainWindow::dispPlot()
{
    int i;

    i = radioButton->checkedId() + 1;
    if(i<MSA9020_COUNT) {
        chname = "CHA"+QString::number(i);
    }

    if(i>=MSA9020_COUNT) {
        chname = "CHB"+QString::number(i-MSA9020_COUNT);
    }

    dispRecod(chname);
}

// 浓度记录显示
void MainWindow::dispRecod(QString cchname)
{
    float minValue = 0, maxValue = 0;
    int recsize = ui->comboBox->currentText().toInt();
    int page = ui->spinBox->value();

    QString sqlcmd = QString("select count(*) "
                             "from concentration "
                             "where chname='%1' ").arg(cchname);

    QSqlQuery query;
    query.exec(sqlcmd);
    query.next();
    int recs = query.value(0).toInt();
    if(recs % recsize != 0)
        pages = recs / recsize+1;
    else
        pages = recs / recsize;
    ui->spinBox->setMaximum(pages);
    ui->label_page->setText("页，共"+QString::number(pages)+"页");
    sqlcmd = QString("select datetime(numtime, 'unixepoch', 'localtime'), numvalue, numunit, numtime "
                     "from concentration "
                     "where chname = '%1' "
                     "order by numtime desc "
                     "limit %2, %3").arg(cchname).arg(recsize*(page-1)).arg(recsize);
    query = model->query();
    model->setQuery(sqlcmd);

    model->setHeaderData(0, Qt::Horizontal, tr("日期"));
    model->setHeaderData(1, Qt::Horizontal, tr("数值"));
    model->setHeaderData(2, Qt::Horizontal, tr("单位"));
    ui->tableView->hideColumn(3);

    mGraphDB->data()->clear();
    QSqlRecord record;
    int rc = model->rowCount();
    for(int i=0; i<rc; i++) {
        record = model->record(i);
        mGraphDB->addData(record.value(3).toInt(), record.value(1).toDouble());
        minValue = minValue>record.value(1).toDouble() ? record.value(1).toDouble() : minValue;
        maxValue = maxValue<record.value(1).toDouble() ? record.value(1).toDouble() : maxValue;
    }
    ui->mPlot->yAxis->setRange(minValue-10, maxValue+10);
    ui->mPlot->xAxis->rescale();
    ui->mPlot->xAxis->setRange(model->record(rc-1).value(3).toInt(), model->record(0).value(3).toInt());
    //ui->mPlot->replot();
    mGraphDB->setVisible(true);

    if(!plotPlay) {
        plotTitleName->setText("历史趋势图");
    } else
        plotTitleName->setText("实时趋势图");
    ui->mPlot->replot();
}

// 报警记录显示
void MainWindow::dispAlarm()
{
    int recsize = ui->comboBox_Recoders->currentText().toInt();
    int page = ui->spinBox_Pages->value();

    QString sqlcmd = QString("select count(*) from alarm ");
    QSqlQuery query;
    query.exec(sqlcmd);
    query.next();
    int recs = query.value(0).toInt();
    if(recs % recsize != 0)
        alarmPages = recs / recsize+1;
    else
        alarmPages = recs / recsize;
    ui->spinBox_Pages->setMaximum(alarmPages);
    ui->label_page_alarm->setText("页，共"+QString::number(alarmPages)+"页");
    sqlcmd = QString("select datetime(numtime, 'unixepoch', 'localtime'), chname, numvalue "
                     "from alarm "
                     "order by numtime desc "
                     "limit %1, %2").arg(recsize*(page-1)).arg(recsize);
    alarmmodel->setQuery(sqlcmd);
}

// 退出系统
void MainWindow::on_action_exit_triggered()
{
    int ret = QMessageBox::question(this, tr("确认"), tr("请问是否退出程序？"), QMessageBox::Yes | QMessageBox::No);
    if(ret == QMessageBox::Yes)
        this->close();
}

void MainWindow::on_plotPaused_clicked()
{
    plotPlay = false;
    ui->plotPlay->setEnabled(true);
    ui->plotPaused->setEnabled(false);
}

void MainWindow::on_plotPlay_clicked()
{
    plotPlay = true;
    ui->plotPlay->setEnabled(false);
    ui->plotPaused->setEnabled(true);
}

void MainWindow::on_pageEnd_clicked()
{
    ui->spinBox->setValue(pages);
    on_plotPaused_clicked(); // 暂停实时数据显示
    dispRecod(chname);
}

void MainWindow::on_pageFrist_clicked()
{
    ui->spinBox->setValue(1);
    on_plotPaused_clicked(); // 暂停实时数据显示
    dispRecod(chname);
}

void MainWindow::on_pagePrevious_clicked()
{
    if(ui->spinBox->value()>1) {
        ui->spinBox->setValue(ui->spinBox->value()-1);
        on_plotPaused_clicked(); // 暂停实时数据显示
        dispRecod(chname);
    }
}

void MainWindow::on_pageNext_clicked()
{
    ui->spinBox->setValue(ui->spinBox->value()+1);
    on_plotPaused_clicked(); // 暂停实时数据显示
    dispRecod(chname);
}

// 删除历史记录
void MainWindow::deleteRecoder()
{
    QSqlQuery query;
    QDateTime dateTime = QDateTime::currentDateTime();
    uint  now = dateTime.toTime_t();//当前时间转化为秒
    QString sqlcmd = QString("delete from concentration where numtime<=%1").arg(now-saveday*24*60*60);
    query.exec(sqlcmd);

    sqlcmd = QString("delete from alarm where numtime<=%1").arg(now-saveday*24*60*60);
    query.exec(sqlcmd);
}

void MainWindow::on_pushButtonGoto_clicked()
{
    on_plotPaused_clicked(); // 暂停实时数据显示
    dispRecod(chname);
}

void MainWindow::on_pushButtonGoto_2_clicked()
{
    dispAlarm();
}

void MainWindow::on_pageFrist_2_clicked()
{
    ui->spinBox_Pages->setValue(1);
    dispAlarm();
}

void MainWindow::on_pagePrevious_2_clicked()
{
    if(ui->spinBox_Pages->value()>1) {
        ui->spinBox_Pages->setValue(ui->spinBox_Pages->value()-1);
        dispAlarm();
    }
}

void MainWindow::on_pageNext_2_clicked()
{
    ui->spinBox_Pages->setValue(ui->spinBox_Pages->value()+1);
    dispAlarm();
}

void MainWindow::on_pageEnd_2_clicked()
{
    ui->spinBox_Pages->setValue(alarmPages);
    dispAlarm();
}

void MainWindow::on_spinBox_valueChanged(int arg1)
{
    if(arg1==1) {
        ui->pageFrist->setEnabled(false);
        ui->pagePrevious->setEnabled(false);
    } else {
        ui->pageFrist->setEnabled(true);
        ui->pagePrevious->setEnabled(true);
    }

    if(arg1>=pages) {
        ui->pageNext->setEnabled(false);
        ui->pageEnd->setEnabled(false);
    } else {
        ui->pageNext->setEnabled(true);
        ui->pageEnd->setEnabled(true);
    }

    if(arg1 == 0) {
        ui->pageFrist->setEnabled(false);
        ui->pagePrevious->setEnabled(false);
        ui->pageNext->setEnabled(false);
        ui->pageEnd->setEnabled(false);
    }
}

void MainWindow::on_spinBox_Pages_valueChanged(int arg1)
{
    if(arg1==1) {
        ui->pageFrist_2->setEnabled(false);
        ui->pagePrevious_2->setEnabled(false);
    } else {
        ui->pageFrist_2->setEnabled(true);
        ui->pagePrevious_2->setEnabled(true);
    }
    if(arg1>=alarmPages) {
        ui->pageNext_2->setEnabled(false);
        ui->pageEnd_2->setEnabled(false);
    } else {
        ui->pageNext_2->setEnabled(true);
        ui->pageEnd_2->setEnabled(true);
    }
}

// 关于窗口
void MainWindow::on_action_about_triggered()
{
    AboutDialog *aboutdlg = new AboutDialog;
    aboutdlg->setWindowModality(Qt::ApplicationModal); //顶端窗口
    QObject::connect(this,SIGNAL(sendpre(bool)),aboutdlg,SLOT(getpre(bool)));
    emit sendpre(!checkBuy());
    aboutdlg->show();
    // 在主窗口中间显示
    int x =this->x() + (this->width() - aboutdlg->width()) / 2;
    int y =this->y() + (this->height() - aboutdlg->height()) / 2;
    aboutdlg->move(x, y);
}


void MainWindow::on_action_reboot_triggered()
{
    int ret = QMessageBox::question(this, tr("确认"), tr("请问是否重启动服务器？"), QMessageBox::Yes | QMessageBox::No);
    if(ret == QMessageBox::Yes)
        system("/sbin/reboot");
}

void MainWindow::on_action_halt_triggered()
{
    int ret = QMessageBox::question(this, tr("确认"), tr("请问是否关闭服务器？"), QMessageBox::Yes | QMessageBox::No);
    if(ret == QMessageBox::Yes)
        system("/sbin/shutdown -h now");
}

// 连接MODBUS从机
void MainWindow::connectSlave()
{
    if (modbusDevice->state() != QModbusDevice::ConnectedState) {
        QSettings configIni(QCoreApplication::applicationDirPath()+"/config.ini", QSettings::IniFormat);
        QString com = configIni.value("com/portname").toString();
        int baudrate = configIni.value("com/baudrate").toInt();
        modbusDevice->setConnectionParameter(QModbusDevice::SerialPortNameParameter, com);
        modbusDevice->setConnectionParameter(QModbusDevice::SerialParityParameter, QSerialPort::NoParity);
        if(baudrate == 9600) {
            modbusDevice->setConnectionParameter(QModbusDevice::SerialBaudRateParameter, QSerialPort::Baud9600);
        } else {
            modbusDevice->setConnectionParameter(QModbusDevice::SerialBaudRateParameter, QSerialPort::Baud38400);
        }
        modbusDevice->setConnectionParameter(QModbusDevice::SerialDataBitsParameter, QSerialPort::Data8);
        modbusDevice->setConnectionParameter(QModbusDevice::SerialStopBitsParameter, QSerialPort::OneStop);
        modbusDevice->setTimeout(1000); // 超时时间
        modbusDevice->setNumberOfRetries(3); // 重连次数

        if (!modbusDevice->connectDevice()) {
            ComStatic->setText(tr("连接失败: ") + modbusDevice->errorString());
            qDebug() << modbusDevice->errorString();
        } else {
            //读MSA卡的单位
            foreach(quint8 i,m.cards.keys()) {
                readMsaUnit(i);
                QCoreApplication::processEvents();
            }
            // 开始数据查询轮询
            timer->start(configIni.value("com/delytime").toInt()*1000);
        }
    } else {
        modbusDevice->disconnectDevice();
    }
    qDebug() << modbusDevice->state();
}

//MODBUS连接状态
void MainWindow::onStateChanged(int state)
{
    bool connected = (state != QModbusDevice::UnconnectedState);
    ui->action_openCom->setEnabled(!connected);
    ui->action_closeCom->setEnabled(connected);

    if (state == QModbusDevice::UnconnectedState)
    {
        ComStatic->setText(tr("连接关闭. "));
//        ui->action_openCom->setEnabled(true);
//        ui->action_closeCom->setEnabled(false);
        timer->stop();
    }
    else if (state == QModbusDevice::ConnectedState)
    {
        QString cn = modbusDevice->connectionParameter(QModbusDevice::SerialPortNameParameter).toString();
        ComStatic->setText(tr("连接成功: ") + cn);
//        ui->action_openCom->setEnabled(false);
//        ui->action_closeCom->setEnabled(true);
    }
    qDebug() << modbusDevice->state();
}

// 复位，消除MSA9020卡状态灯
void MainWindow::on_action_alarm_triggered()
{
    bool ok;
    int modbusaddr = QInputDialog::getInt(this, tr("卡槽复位"),
                                          tr("请输入卡槽Modbus地址:"), 1, 0, 254, 1, &ok);
    if (ok) {
        setLedTxOn();
        modbusDevice->sendRawRequest(QModbusRequest(QModbusPdu::MsaPermits,QByteArray::fromHex("")), modbusaddr);

        QString s = QString("  地址%1的卡槽已复位.").arg(modbusaddr);
        ui->statusBar->showMessage(s, 3000);
    }
}

//读MSA卡的单位，在“connectSlave()”函数中调用
void MainWindow::readMsaUnit(int slaveAddr)
{
    if (!modbusDevice)
        return;

    setLedTxOn();
    // A通道测量单位地址:13;
    if (auto *replyA = modbusDevice->sendReadRequest(QModbusDataUnit(QModbusDataUnit::HoldingRegisters, UNIT_MEASUR_CHB, 1)
                       , slaveAddr)) {
        if (!replyA->isFinished()) {
            if (replyA->error() == QModbusDevice::NoError)
                connect(replyA, &QModbusReply::finished, this, &MainWindow::readReadyUnitA);
        } else
            delete replyA; // broadcast replies return immediately
    } else {
        statusBar()->showMessage(tr("Read error: ") + modbusDevice->errorString(), 5000);
    }

    //setLedTxOn();
    // B通道测量单位地址:57
    if (auto *replyB = modbusDevice->sendReadRequest(QModbusDataUnit(QModbusDataUnit::HoldingRegisters, UNIT_MEASUR_CHB, 1)
                       , slaveAddr)) {
        if (!replyB->isFinished()) {
            if (replyB->error() == QModbusDevice::NoError)
                connect(replyB, &QModbusReply::finished, this, &MainWindow::readReadyUnitB);
        } else
            delete replyB; // broadcast replies return immediately
    } else {
        statusBar()->showMessage(tr("Read error: ") + modbusDevice->errorString(), 5000);
    }
}

// A通道测量单位槽函数
void MainWindow::readReadyUnitA()
{
    setLedRxOn();
    auto reply = qobject_cast<QModbusReply *>(sender());
    if (!reply)
        return;

    if (reply->error() == QModbusDevice::NoError) {
        const QModbusDataUnit unit = reply->result();
        const QString entry = tr("Modbus address: %1, A Unit value: %2").arg(reply->serverAddress())
                              .arg(QString::number(unit.value(0)));
        m.setUnitMeasurCha(reply->serverAddress(),unit.value(0));
        qDebug() << entry;
    } else if (reply->error() == QModbusDevice::ProtocolError) {
        statusBar()->showMessage(tr("Read A %3 unit response error: %1 (Mobus exception: 0x%2)").
                                 arg(reply->errorString()).
                                 arg(reply->rawResult().exceptionCode(), -1, 16).arg(reply->serverAddress()), 5000);
    } else {
        statusBar()->showMessage(tr("Read A %3 unit response error: %1 (code: 0x%2)").
                                 arg(reply->errorString()).
                                 arg(reply->error(), -1, 16).arg(reply->serverAddress()), 5000);
    }

    reply->deleteLater();
    //ledLabel->setStyleSheet(m_grey_SheetStyle);//改成 灰色圆形
}

// B通道测量单位槽函数
void MainWindow::readReadyUnitB()
{
    setLedRxOn();
    auto reply = qobject_cast<QModbusReply *>(sender());
    if (!reply)
        return;

    if (reply->error() == QModbusDevice::NoError) {
        const QModbusDataUnit unit = reply->result();
        const QString entry = tr("Modbus address: %1, B Unit value: %2").arg(reply->serverAddress())
                              .arg(QString::number(unit.value(0)));
        m.setUnitMeasurChb(reply->serverAddress(),unit.value(0));
        qDebug() << entry;
    } else if (reply->error() == QModbusDevice::ProtocolError) {
        statusBar()->showMessage(tr("Read B %3 unit response error: %1 (Mobus exception: 0x%2)").
                                 arg(reply->errorString()).
                                 arg(reply->rawResult().exceptionCode(), -1, 16).arg(reply->serverAddress()), 5000);
    } else {
        statusBar()->showMessage(tr("Read B %3 unit response error: %1 (code: 0x%2)").
                                 arg(reply->errorString()).
                                 arg(reply->error(), -1, 16).arg(reply->serverAddress()), 5000);
    }

    reply->deleteLater();
}

//读MSA卡的浓度
void MainWindow::readMsaData(int slaveAddr)
{
    if (!modbusDevice)
        return;

    setLedTxOn();
    if (auto *reply = modbusDevice->sendReadRequest(QModbusDataUnit(QModbusDataUnit::HoldingRegisters, CARD_INFO_ADDR, 24)
                      , slaveAddr)) {
        if (!reply->isFinished())
            connect(reply, &QModbusReply::finished, this, &MainWindow::readReady);
        else
            delete reply; // broadcast replies return immediately
    } else {
        statusBar()->showMessage(tr("Read error: ") + modbusDevice->errorString(), 5000);
    }
}

//读MSA卡的浓度数值槽函数
void MainWindow::readReady()
{
    setLedRxOn();
    auto reply = qobject_cast<QModbusReply *>(sender());
    if (!reply)
        return;

    if (reply->error() == QModbusDevice::NoError) {
        const QModbusDataUnit unit = reply->result();
        for (int i = 0, total = int(unit.valueCount()); i < total; ++i) {
            m.cards[reply->serverAddress()].ci.data[i] = unit.value(i);
#ifdef _DEBUG_MODE
            qDebug("%x ",unit.value(i));
#endif

            const QString entry = tr("Modbus address: %1, Index: %2, Value: %3").arg(reply->serverAddress())
                                  .arg(unit.startAddress()+i).arg(unit.value(i));
            qDebug() << entry;
        }
        //m.cards[1].ci.state_drift_chb=1; //test
        // 刷新显示新数据
        m.refreshDisplay(reply->serverAddress());

        // 存储数据
        dbAppendRecord(reply->serverAddress());
        dbAppendAlarm(reply->serverAddress());

        if(plotPlay) {
            int i = radioButton->checkedId() + 1;
            if((m.getChIndex(reply->serverAddress()) == i) || (m.getChIndex(reply->serverAddress()) == i-MSA9020_COUNT))
                dispPlot();
        }

        dispAlarm();

    } else if (reply->error() == QModbusDevice::ProtocolError) {
        statusBar()->showMessage(tr("Read response error: %1 (Mobus exception: 0x%2)").
                                 arg(reply->errorString()).
                                 arg(reply->rawResult().exceptionCode(), -1, 16), 5000);
    } else if (reply->error() == QModbusDevice::TimeoutError) {
        m.displayOvertime(reply->serverAddress());
    } else {
        statusBar()->showMessage(tr("Read response error: %1 (code: 0x%2)").
                                 arg(reply->errorString()).
                                 arg(reply->error(), -1, 16), 5000);
    }

    reply->deleteLater();
}



bool checkBuy()
{
#ifdef _DEBUG_MODE
    return true;
#endif
    QSettings ini("/home/pi/.config/network000", QSettings::IniFormat);
    // 串口参数设置
    QString MAC = ini.value("setting/mac", "").toString().toUpper();

    QList<QNetworkInterface> networkInterface = QNetworkInterface::allInterfaces();
    for (QList<QNetworkInterface>::const_iterator it = networkInterface.constBegin(); it != networkInterface.constEnd(); ++it)
    {
        if(((*it).humanReadableName() == "eth0") and ((*it).hardwareAddress().toUpper() == MAC))
            return true;
    }
    return false;
}

bool MainWindow::createConnection()
{
    db = QSqlDatabase::addDatabase("QSQLITE");
    db.setDatabaseName(QCoreApplication::applicationDirPath()+"/msa.db");
    if (!db.open())
        return false;

    QSqlQuery query;
    // 创建浓度表
    query.exec("CREATE TABLE IF NOT EXISTS concentration("
               "    id integer primary key AUTOINCREMENT NOT NULL,"
               "    chname varchar(10) NOT NULL,"
               "    numtime timestamp NOT NULL,"
               "    numvalue float NOT NULL,"
               "    numunit varchar(10) NOT NULL)");
    // 创建报警记录表
    query.exec("CREATE TABLE IF NOT EXISTS alarm("
               "    id integer primary key AUTOINCREMENT NOT NULL,"
               "    chname varchar(10) NOT NULL,"
               "    numtime timestamp NOT NULL,"
               "    numvalue varchar(250))");
    return true;
}

// 将浓度值保存数据库，参数：数据时间，通道名，浓度值
bool MainWindow::dbAppendRecord(quint8 serverAddress)
{
    QDateTime dateTime = QDateTime::currentDateTime();
    uint  numTime = dateTime.toTime_t();//当前时间转化为秒

    QString chName = "CHA"+QString::number(m.getChIndex(serverAddress));
    float numValue = m.getValueCha(serverAddress);
    QString numUnit = m.getUnitMeasurCha(serverAddress);
    QSqlQuery query;
    QString sqlstr = QString("insert into concentration(chname,numtime,numvalue,numunit) "
                             "values('%1', %2, %3, '%4')").arg(chName).arg(numTime).arg(numValue).arg(numUnit);
    query.exec(sqlstr);
    //qDebug() << sqlstr;

    chName = "CHB"+QString::number(m.getChIndex(serverAddress));
    numValue = m.getValueChb(serverAddress);
    numUnit = m.getUnitMeasurChb(serverAddress);

    sqlstr = QString("insert into concentration(chname,numtime,numvalue,numunit) "
                     "values('%1', %2, %3, '%4')").arg(chName).arg(numTime).arg(numValue).arg(numUnit);
    return query.exec(sqlstr);
    //qDebug() << sqlstr;

}

void MainWindow::playWarningSound()
{
    int i;
    if(player->state()==QMediaPlayer::PlayingState)
        return;

    QSettings configIni(QCoreApplication::applicationDirPath()+"/config.ini", QSettings::IniFormat);
    bool sound_random = configIni.value("sys/mp3_random", false).toBool();
    int sound_index = configIni.value("sys/mp3", 0).toInt();
    if(sound_random)
        i = QRandomGenerator::global()->bounded(fileInfo->count());
    else
        i = sound_index;

    playlist->setCurrentIndex(i);
    player->setVolume(volume);
#ifdef _DEBUG_MODE
    qDebug() << "Mp3(" << i << "): " << fileInfo->at(i).filePath();
    qDebug() << "Vol: " << volume;
#endif
    player->play();

}

// 将报警记录保存数据库，参数：数据时间，通道名，报警内容
void MainWindow::dbAppendAlarm(quint8 serverAddress)
{
    QSqlQuery query;
    QString chName, numValue;

    QDateTime dateTime = QDateTime::currentDateTime();
    uint  numTime = dateTime.toTime_t();//当前时间转化为秒

    if(!m.cards[serverAddress].fa && m.getStateFailCha(serverAddress) > 0) {
        chName = "CHA"+QString::number(m.getChIndex(serverAddress));
        numValue = m.SFC[m.getStateFailCha(serverAddress)][1];
        QString sqlstr = QString("insert into alarm(chname, numtime, numvalue) "
                                 "values('%1', %2, '%3')").arg(chName).arg(numTime).arg(numValue);
        query.exec(sqlstr);
        m.cards[serverAddress].fa = true;
        playWarningSound();
    }

    if(!m.cards[serverAddress].fb && m.getStateFailChb(serverAddress) > 0) {
        chName = "CHB"+QString::number(m.getChIndex(serverAddress));
        numValue = m.SFC[m.getStateFailChb(serverAddress)][1];
        QString sqlstr = QString("insert into alarm(chname, numtime, numvalue) "
                                 "values('%1', %2, '%3')").arg(chName).arg(numTime).arg(numValue);
        query.exec(sqlstr);
        m.cards[serverAddress].fb = true;
        playWarningSound();
    }

    if(!m.cards[serverAddress].da && m.getStateDriftCha(serverAddress) > 0) {
        chName = "CHA"+QString::number(m.getChIndex(serverAddress));
        numValue = "传感器漂移";
        QString sqlstr = QString("insert into alarm(chname, numtime, numvalue) "
                                 "values('%1', %2, '%3')").arg(chName).arg(numTime).arg(numValue);
        query.exec(sqlstr);
        m.cards[serverAddress].da = true;
        playWarningSound();
    }

    if(!m.cards[serverAddress].db && m.getStateDriftChb(serverAddress) > 0) {
        chName = "CHB"+QString::number(m.getChIndex(serverAddress));
        numValue = "传感器漂移";
        QString sqlstr = QString("insert into alarm(chname, numtime, numvalue) "
                                 "values('%1', %2, '%3')").arg(chName).arg(numTime).arg(numValue);
        query.exec(sqlstr);
        m.cards[serverAddress].db = true;
        playWarningSound();
    }

    if(!m.cards[serverAddress].ea && m.getStateEEEECha(serverAddress) > 0) {
        chName = "CHA"+QString::number(m.getChIndex(serverAddress));
        numValue = "EEEE 超量程";
        QString sqlstr = QString("insert into alarm(chname, numtime, numvalue) "
                                 "values('%1', %2, '%3')").arg(chName).arg(numTime).arg(numValue);
        query.exec(sqlstr);
        m.cards[serverAddress].ea = true;
        playWarningSound();
    }

    if(!m.cards[serverAddress].eb && m.getStateEEEEChb(serverAddress) > 0) {
        chName = "CHB"+QString::number(m.getChIndex(serverAddress));
        numValue = "EEEE 超量程";
        QString sqlstr = QString("insert into alarm(chname, numtime, numvalue) "
                                 "values('%1', %2, '%3')").arg(chName).arg(numTime).arg(numValue);
        query.exec(sqlstr);
        m.cards[serverAddress].eb = true;
        playWarningSound();
    }

    if(!m.cards[serverAddress].ca && m.getCautionCha(serverAddress) > 0) {
        chName = "CHA"+QString::number(m.getChIndex(serverAddress));
        numValue = QString("小心报警,报警值: %1").arg(m.getValueCha(serverAddress));
        QString sqlstr = QString("insert into alarm(chname, numtime, numvalue) "
                                 "values('%1', %2, '%3')").arg(chName).arg(numTime).arg(numValue);
        query.exec(sqlstr);
        m.cards[serverAddress].ca = true;
        playWarningSound();
    }

    if(!m.cards[serverAddress].cb && m.getCautionChb(serverAddress) > 0) {
        chName = "CHB"+QString::number(m.getChIndex(serverAddress));
        numValue =  QString("小心报警,报警值: %1").arg(m.getValueChb(serverAddress));
        QString sqlstr = QString("insert into alarm(chname, numtime, numvalue) "
                                 "values('%1', %2, '%3')").arg(chName).arg(numTime).arg(numValue);
        query.exec(sqlstr);
        m.cards[serverAddress].cb = true;
        playWarningSound();
    }

    if(!m.cards[serverAddress].wa && m.getWarningCha(serverAddress) > 0) {
        chName = "CHA"+QString::number(m.getChIndex(serverAddress));
        numValue = QString("预报警,报警值: %1").arg(m.getValueCha(serverAddress));
        QString sqlstr = QString("insert into alarm(chname, numtime, numvalue) "
                                 "values('%1', %2, '%3')").arg(chName).arg(numTime).arg(numValue);
        query.exec(sqlstr);
        m.cards[serverAddress].wa = true;
        //playWarningSound();
    }

    if(!m.cards[serverAddress].wb && m.getWarningChb(serverAddress) > 0) {
        chName = "CHB"+QString::number(m.getChIndex(serverAddress));
        numValue = QString("预报警,报警值: %1").arg(m.getValueChb(serverAddress));
        QString sqlstr = QString("insert into alarm(chname, numtime, numvalue) "
                                 "values('%1', %2, '%3')").arg(chName).arg(numTime).arg(numValue);
        query.exec(sqlstr);
        m.cards[serverAddress].wb = true;
        //playWarningSound();
    }

    if(!m.cards[serverAddress].aa && m.getAlarmCha(serverAddress) > 0) {
        chName = "CHA"+QString::number(m.getChIndex(serverAddress));
        numValue = QString("高报警,报警值: %1").arg(m.getValueCha(serverAddress));
        QString sqlstr = QString("insert into alarm(chname, numtime, numvalue) "
                                 "values('%1', %2, '%3')").arg(chName).arg(numTime).arg(numValue);
        query.exec(sqlstr);
        m.cards[serverAddress].aa = true;
        playWarningSound();
    }

    if(!m.cards[serverAddress].ab && m.getAlarmChb(serverAddress) > 0) {
        chName = "CHB"+QString::number(m.getChIndex(serverAddress));
        numValue = QString("高报警,报警值: %1").arg(m.getValueChb(serverAddress));
        QString sqlstr = QString("insert into alarm(chname, numtime, numvalue) "
                                 "values('%1', %2, '%3')").arg(chName).arg(numTime).arg(numValue);
        query.exec(sqlstr);
        m.cards[serverAddress].ab = true;
        playWarningSound();
    }

    if(!m.cards[serverAddress].e1a && m.getErrorE1Cha(serverAddress) > 0) {
        chName = "CHA"+QString::number(m.getChIndex(serverAddress));
        numValue = "E1 零信号和满量程之间的信号差<2mV";
        QString sqlstr = QString("insert into alarm(chname, numtime, numvalue) "
                                 "values('%1', %2, '%3')").arg(chName).arg(numTime).arg(numValue);
        query.exec(sqlstr);
        m.cards[serverAddress].e1a = true;
        playWarningSound();
    }

    if(!m.cards[serverAddress].e1b && m.getErrorE1Chb(serverAddress) > 0) {
        chName = "CHB"+QString::number(m.getChIndex(serverAddress));
        numValue = "E1 零信号和满量程之间的信号差<2mV";
        QString sqlstr = QString("insert into alarm(chname, numtime, numvalue) "
                                 "values('%1', %2, '%3')").arg(chName).arg(numTime).arg(numValue);
        query.exec(sqlstr);
        m.cards[serverAddress].e1b = true;
        playWarningSound();
    }

    if(!m.cards[serverAddress].e2a && m.getErrorE2Cha(serverAddress) > 0) {
        chName = "CHA"+QString::number(m.getChIndex(serverAddress));
        numValue = "E2 零信号和满量程之间的信号差>=200mV";
        QString sqlstr = QString("insert into alarm(chname, numtime, numvalue) "
                                 "values('%1', %2, '%3')").arg(chName).arg(numTime).arg(numValue);
        query.exec(sqlstr);
        m.cards[serverAddress].e2a = true;
        playWarningSound();
    }

    if(!m.cards[serverAddress].e2b && m.getErrorE2Chb(serverAddress) > 0) {
        chName = "CHB"+QString::number(m.getChIndex(serverAddress));
        numValue = "E2 零信号和满量程之间的信号差>=200mV";
        QString sqlstr = QString("insert into alarm(chname, numtime, numvalue) "
                                 "values('%1', %2, '%3')").arg(chName).arg(numTime).arg(numValue);
        query.exec(sqlstr);
        m.cards[serverAddress].e2b = true;
        playWarningSound();
    }

    if(!m.cards[serverAddress].e3a && m.getErrorE3Cha(serverAddress) > 0) {
        chName = "CHA"+QString::number(m.getChIndex(serverAddress));
        numValue = "E3 零信号和满量程之间的信号差 3mV>n>2mV";
        QString sqlstr = QString("insert into alarm(chname, numtime, numvalue) "
                                 "values('%1', %2, '%3')").arg(chName).arg(numTime).arg(numValue);
        query.exec(sqlstr);
        m.cards[serverAddress].e3a = true;
        playWarningSound();
    }

    if(!m.cards[serverAddress].e3b && m.getErrorE3Chb(serverAddress) > 0) {
        chName = "CHB"+QString::number(m.getChIndex(serverAddress));
        numValue = "E3 零信号和满量程之间的信号差 3mV>n>2mV";
        QString sqlstr = QString("insert into alarm(chname, numtime, numvalue) "
                                 "values('%1', %2, '%3')").arg(chName).arg(numTime).arg(numValue);
        query.exec(sqlstr);
        m.cards[serverAddress].e3b = true;
        playWarningSound();
    }

    if(!m.cards[serverAddress].e4a && m.getErrorE4Cha(serverAddress) > 0) {
        chName = "CHA"+QString::number(m.getChIndex(serverAddress));
        numValue = "E4 输入信号高于 +300mV";
        QString sqlstr = QString("insert into alarm(chname, numtime, numvalue) "
                                 "values('%1', %2, '%3')").arg(chName).arg(numTime).arg(numValue);
        query.exec(sqlstr);
        m.cards[serverAddress].e4a = true;
        playWarningSound();
    }

    if(!m.cards[serverAddress].e4b && m.getErrorE4Chb(serverAddress) > 0) {
        chName = "CHB"+QString::number(m.getChIndex(serverAddress));
        numValue = "E4 输入信号高于 +300mV";
        QString sqlstr = QString("insert into alarm(chname, numtime, numvalue) "
                                 "values('%1', %2, '%3')").arg(chName).arg(numTime).arg(numValue);
        query.exec(sqlstr);
        m.cards[serverAddress].e4b = true;
        playWarningSound();
    }
}

void MainWindow::mPlotMoveEvent(QMouseEvent *e)
{
    /* 获取光标位置 */
    int x_pos = e->pos().x();

    /* 转化为坐标系位置 */
    qint64 xv = ui->mPlot->xAxis->pixelToCoord(x_pos);
    double y;
    QString str;

    int start = 0, end = ui->mPlot->graph(0)->data()->size();
    /* 二分查找 */
    do {
        if (xv <= ui->mPlot->graph(0)->data()->at((start + end)/2)->key)
            end = (start + end) / 2;
        else
            start = (start + end) / 2;
        y = ui->mPlot->graph(0)->data()->at(end)->value;

    } while(end - start > 1);
    QDateTime d;
    /* 显示文本 */
    str += QString("时间:%1\n浓度:%2").arg(d.fromMSecsSinceEpoch(xv).toString("yyyy-MM-dd hh:mm:ss")).arg(QString::asprintf("%.3f", y));

    QToolTip::showText(cursor().pos(), str, ui->mPlot);

}

void MainWindow::setLedRxOn()
{
    ledRx->setStyleSheet("color:rgb(0,255,0);");
    if(!ledRxOff->isActive())
        ledRxOff->start(150);
}

void MainWindow::setLedTxOn()
{
    ledTx->setStyleSheet("color:rgb(0,255,0);");
    if(!ledTxOff->isActive())
        ledTxOff->start(150);
}

void MainWindow::on_actionTEST_triggered()
{
    //player->setMedia();
}

void MainWindow::setvol(int v)
{
    player->setVolume(v);
    volume = v;
    //qDebug() << "Vol:" << v;
}

void MainWindow::on_actionVol_triggered()
{
    dlgvol->move(cursor().pos());
    dlgvol->show();
}

void MainWindow::on_action_triggered()
{
    QProcess process;
    process.startDetached("python3 /usr/bin/onboard");
}

void MainWindow::on_action_Sound_triggered()
{
    playWarningSound();
}
